#include <node.h>
#include "MDNS_Object.h"
#include <string>

using namespace v8;
using namespace std;

struct Baton {
	uv_work_t request;
	Persistent<Function> callback;
	MDNS_Object *wrapper;
	struct mdnsd *svr;
	MDNS::Reply_Handler *reply_handler;
	MDNS::Announce_Handler *announce_handler;
	ACE_CString *host_name;
	ACE_CString *instance_name;
	ACE_CString *service_name;
	ACE_CString *ip;
	ACE_CString *port;
	char *txt[];
};
MDNS_Object::MDNS_Object () {};
MDNS_Object::~MDNS_Object () {};

static void MDNS_Async (uv_work_t *req) {
	ACE_DEBUG ((LM_DEBUG, "MDNS async\n"));
	Baton *baton = static_cast<Baton *> (req->data);

	if (!baton->host_name)
		baton->host_name = new ACE_CString ("some-random-host.local");
	if (!baton->instance_name)
		baton->instance_name = new ACE_CString ("LGTV");
	if (!baton->service_name)
		baton->service_name = new ACE_CString ("_airplay._tcp.local");
	if (!baton->ip)
		baton->ip = new ACE_CString ("127.0.0.1");
	if (!baton->port)
		baton->port = new ACE_CString ("50000");

	const char *txt[] = {
		"srcvers=101.10",
		"features=0x39f7",
		"deviceid=E0:CB:4E:AE:E0:31",
		NULL
	};

	ACE_DEBUG ((LM_DEBUG, "Host name - %s\n", baton->host_name->c_str ()));
	ACE_DEBUG ((LM_DEBUG, "Instance name - %s\n", baton->instance_name->c_str ()));
	ACE_DEBUG ((LM_DEBUG, "Service name - %s\n", baton->service_name->c_str ()));
	ACE_DEBUG ((LM_DEBUG, "IP - %s\n", baton->ip->c_str ()));
	ACE_DEBUG ((LM_DEBUG, "Port - %d\n", atoi (baton->port->c_str ())));

	baton->svr = mdnsd_start ();

	mdnsd_set_hostname (baton->svr, baton->host_name->c_str (), inet_addr
		(baton->ip->c_str ()));

	ACE_Reactor *reactor = ACE_Reactor::instance ();

	baton->announce_handler = new MDNS::Announce_Handler (reactor);
	baton->announce_handler->open (baton->svr);

	baton->reply_handler = new MDNS::Reply_Handler (reactor);
	baton->reply_handler->open (baton->svr);

	struct mdns_service *svc = mdnsd_register_svc (baton->svr,
		baton->instance_name->c_str (), baton->service_name->c_str (),
		atoi (baton->port->c_str ()), NULL, txt);

	mdns_service_destroy (svc);

	reactor->run_reactor_event_loop ();
}

static void MDNS_Async_After (uv_work_t *req) {
	Baton *baton = static_cast<Baton *> (req->data);

	// close handlers
	baton->reply_handler->handle_close ();
	baton->announce_handler->handle_close ();

	Handle<Value> argv[] = { Null () };
	baton->callback->Call (Context::GetCurrent ()->Global (), 1, argv);
	baton->callback.Dispose ();
	delete baton;
}

void MDNS_Object::Init (Handle<Object> target) {
	Local<FunctionTemplate> tpl = FunctionTemplate::New (New);
	tpl->SetClassName (String::NewSymbol ("MDNSObject"));
	tpl->InstanceTemplate ()->SetInternalFieldCount (1);

	tpl->PrototypeTemplate ()->Set (String::NewSymbol ("start"),
		FunctionTemplate::New (Start)->GetFunction ());

	tpl->PrototypeTemplate ()->Set (String::NewSymbol ("stop"),
		FunctionTemplate::New (Stop)->GetFunction ());

	Persistent<Function> constructor = Persistent<Function>::New
		(tpl->GetFunction());
	target->Set (String::NewSymbol ("MDNSObject"), constructor);
}

Handle<Value> MDNS_Object::New (const Arguments& args) {

	ACE_LOG_MSG->priority_mask (LM_ERROR, ACE_Log_Msg::PROCESS);

	ACE_DEBUG ((LM_DEBUG, "MDNS object created\n"));
	HandleScope scope;

	MDNS_Object *obj = new MDNS_Object ();
	obj->Wrap (args.This ());

	return args.This ();
}

Handle<Value> MDNS_Object::Start (const Arguments &args) {
	ACE_DEBUG ((LM_DEBUG, "MDNS service started\n"));

	HandleScope scope;
	MDNS_Object *obj = ObjectWrap::Unwrap<MDNS_Object> (args.Holder ());

	Handle<Function> callback = Handle<Function>::Cast(args[0]);

	Baton* baton = new Baton ();
	baton->request.data = baton;
	baton->callback = Persistent<Function>::New(callback);
	baton->wrapper = obj;

	String::AsciiValue host_name (args[0]->ToString());
	baton->host_name = new ACE_CString (*host_name);

	String::AsciiValue instance_name (args[1]->ToString());
	baton->instance_name = new ACE_CString (*instance_name);

	String::AsciiValue service_name (args[2]->ToString());
	baton->service_name = new ACE_CString (*service_name);

	String::AsciiValue ip (args[3]->ToString());
	baton->ip = new ACE_CString (*ip);

	String::AsciiValue port (args[4]->ToString());
	baton->port = new ACE_CString (*port);
	
	uv_queue_work (uv_default_loop (), &baton->request, MDNS_Async, MDNS_Async_After);

	return Undefined ();
}

Handle<Value> MDNS_Object::Stop (const Arguments &args) {
	ACE_Reactor *reactor = ACE_Reactor::instance ();

	reactor->end_reactor_event_loop ();

	return Undefined ();
}
